﻿using OSGeo.GDAL;
using OSGeo.OGR;
using OSGeo.OSR;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace ConsoleApp
{
    public class UnifiedGeoTiffSplitter
    {
        public const string GEO_TIFF_SPLIT_DIR_NAME = "tiffsplits";
        public const string GRID_METADATA_JSON_FILE_NAME = "grid_metadata.json";

        public static void SplitMultipleGeoTiffs(string[] inputPaths, string outputDir,
                                      int tileSize = 512, bool overwrite = false)
        {
            if (inputPaths == null || inputPaths.Length == 0)
                throw new ArgumentException("至少需要一个输入文件");

            Directory.CreateDirectory(outputDir);
            GdalConfiguration.ConfigureGdal();
            GdalConfiguration.ConfigureOgr();
            Gdal.AllRegister();
            Ogr.RegisterAll();

            // 步骤1: 分析所有文件的地理信息
            List<FileGeoInfos> fileInfos = new List<FileGeoInfos>();
            foreach (string path in inputPaths)
            {
                using (Dataset ds = Gdal.Open(path, Access.GA_ReadOnly))
                {
                    if (ds == null) throw new InvalidOperationException($"无法读取文件: {path}");
                    fileInfos.Add(new FileGeoInfos(ds, path));
                }
            }

            // 步骤2: 计算统一网格系统
            UnifiedGridSystem gridSystem = new UnifiedGridSystem(fileInfos, tileSize);

            // 步骤3: 为每个文件处理网格
            foreach (var fileInfo in fileInfos)
            {
                using (Dataset dataset = Gdal.Open(fileInfo.FilePath, Access.GA_ReadOnly))
                {
                    string fileType = GetFileTypeIdentifier(fileInfo.FilePath);
                    ProcessFileForGrid(dataset, gridSystem, outputDir,
                                     fileType, overwrite);
                }
            }

            // 步骤4: 保存网格元数据（包含正确的经纬度）
            File.WriteAllText(
                Path.Combine(outputDir, GRID_METADATA_JSON_FILE_NAME),
                Newtonsoft.Json.JsonConvert.SerializeObject(gridSystem.GridInfo,
                Newtonsoft.Json.Formatting.Indented)
            );
        }

        private static string GetFileTypeIdentifier(string filePath)
        {
            string fileName = Path.GetFileNameWithoutExtension(filePath);
            if (fileName.EndsWith("_T", StringComparison.OrdinalIgnoreCase)) return "T";
            if (fileName.EndsWith("_V", StringComparison.OrdinalIgnoreCase)) return "V";
            return "U"; // 未知类型
        }

        private static void ProcessFileForGrid(Dataset dataset, UnifiedGridSystem gridSystem,
                                           string outputDir, string fileType,
                                           bool overwrite)
        {
            double[] transform = new double[6];
            dataset.GetGeoTransform(transform);

            ColorTable colorTable = null;
            using (Band firstBand = dataset.GetRasterBand(1))
            {
                colorTable = firstBand.GetRasterColorTable()?.Clone();
            }

            foreach (var gridCell in gridSystem.GridInfo.Cells)
            {
                // 计算当前网格在原始图像中的像素范围
                int pixelX = (int)Math.Round((gridCell.TopLeft.X - transform[0]) / transform[1]);
                int pixelY = (int)Math.Round((gridCell.TopLeft.Y - transform[3]) / transform[5]);

                // 检查是否在图像范围内
                if (pixelX < 0 || pixelY < 0 ||
                    pixelX >= dataset.RasterXSize ||
                    pixelY >= dataset.RasterYSize)
                {
                    continue;
                }

                // 计算实际读取区域
                int readWidth = Math.Min(gridSystem.TileSize, dataset.RasterXSize - pixelX);
                int readHeight = Math.Min(gridSystem.TileSize, dataset.RasterYSize - pixelY);

                if (readWidth <= 0 || readHeight <= 0) continue;

                // 生成统一命名的文件名
                string fileName = $"{gridSystem.BaseName}_{gridCell.Row}_{gridCell.Col}_{fileType}.png";
                string filePath = Path.Combine(outputDir, fileName);

                if (!overwrite && File.Exists(filePath))
                {
                    Console.WriteLine($"跳过 {fileName}");
                    continue;
                }
                gridCell.FileTypes.Add(fileName);
                // 读取并处理图块
                ProcessGridTile(dataset, pixelX, pixelY, readWidth, readHeight,
                              colorTable, filePath, gridCell);
            }
        }

        private static void ProcessGridTile(Dataset dataset, int xStart, int yStart,
                                         int width, int height, ColorTable colorTable,
                                         string filePath, GridCellInfo gridCell)
        {
            int bandCount = dataset.RasterCount;
            byte[][] tileData = new byte[bandCount][];

            for (int b = 0; b < bandCount; b++)
            {
                using (Band band = dataset.GetRasterBand(b + 1))
                {
                    tileData[b] = ReadBandData(band, xStart, yStart, width, height);
                }
            }

            // 检查是否为空白切片
            if (IsTileBlank(tileData))
            {
                Console.WriteLine($"跳过空白切片 {Path.GetFileName(filePath)}");
                return;
            }

            // 颜色处理决策
            if (bandCount == 1 && colorTable != null)
            {
                SaveWithColorTable(tileData[0], colorTable, width, height, filePath);
            }
            else if (bandCount >= 3)
            {
                SaveAsTrueColor(tileData[0], tileData[1], tileData[2], width, height, filePath);
            }
            else
            {
                GeneratePseudocolor(tileData[0], width, height, filePath);
            }

            // 更新网格单元信息（记录哪些文件存在该切片）
            //gridCell.FileTypes.Add(Path.GetExtension(filePath).TrimStart('.'));
        }

        // 以下辅助方法与原始GeoTiffSplitter相同
        private static byte[] ReadBandData(Band band, int xStart, int yStart,
                                         int width, int height)
        {
            byte[] buffer = new byte[width * height];
            band.ReadRaster(xStart, yStart, width, height,
                          buffer, width, height, 0, 0);
            return buffer;
        }

        private static void SaveWithColorTable(byte[] data, ColorTable ct,
                                             int width, int height, string path)
        {
            using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed))
            {
                // 设置调色板
                ColorPalette palette = bmp.Palette;
                for (int i = 0; i < ct.GetCount(); i++)
                {
                    ColorEntry ce = ct.GetColorEntry(i);
                    palette.Entries[i] = Color.FromArgb(ce.c4, ce.c1, ce.c2, ce.c3);
                }
                bmp.Palette = palette;

                // 写入数据
                BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height),
                                              ImageLockMode.WriteOnly,
                                              PixelFormat.Format8bppIndexed);
                Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
                bmp.UnlockBits(bmpData);
                bmp.Save(path, ImageFormat.Png);
            }
        }

        private static void SaveAsTrueColor(byte[] r, byte[] g, byte[] b,
                                          int width, int height, string path)
        {
            using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
            {
                BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height),
                                                ImageLockMode.WriteOnly,
                                                PixelFormat.Format24bppRgb);

                unsafe
                {
                    byte* ptr = (byte*)bmpData.Scan0;
                    for (int i = 0; i < width * height; i++)
                    {
                        ptr[2] = r[i]; // R
                        ptr[1] = g[i]; // G
                        ptr[0] = b[i]; // B
                        ptr += 3;
                    }
                }
                bmp.UnlockBits(bmpData);
                bmp.Save(path, ImageFormat.Png);
            }
        }

        public static void GeneratePseudocolor(byte[] data, int width,
                                              int height, string path)
        {
            byte min = byte.MaxValue, max = byte.MinValue;
            foreach (byte val in data)
            {
                if (val < min) min = val;
                if (val > max) max = val;
            }

            using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
            {
                BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height),
                                                ImageLockMode.WriteOnly,
                                                PixelFormat.Format24bppRgb);

                unsafe
                {
                    byte* ptr = (byte*)bmpData.Scan0;
                    for (int i = 0; i < data.Length; i++)
                    {
                        Color color = HeatMapColor(data[i], min, max);
                        ptr[2] = color.R;
                        ptr[1] = color.G;
                        ptr[0] = color.B;
                        ptr += 3;
                    }
                }
                bmp.UnlockBits(bmpData);
                bmp.Save(path, ImageFormat.Png);
            }
        }

        private static Color HeatMapColor(byte value, byte min, byte max)
        {
            float ratio = (float)(value - min) / (max - min);
            return Color.FromArgb(
                (int)(255 * ratio),       // R
                (int)(255 * (1 - ratio)), // G
                0                         // B
            );
        }

        public static bool IsTileBlank(byte[][] tileData)
        {
            if (tileData == null || tileData.Length == 0) return true;

            int bandCount = tileData.Length;
            int pixelCount = tileData[0].Length;

            // 检查所有波段的所有像素是否一致
            byte baseValue = tileData[0][0];
            for (int b = 0; b < bandCount; b++)
            {
                byte[] data = tileData[b];
                if (data.Length != pixelCount) return false;

                foreach (byte val in data)
                {
                    if (val != baseValue) return false;
                }
            }
            return true;
        }
    }

    // 地理信息分析类
    public class FileGeoInfos
    {
        public string FilePath { get; }
        public double MinX { get; }
        public double MaxX { get; }
        public double MinY { get; }
        public double MaxY { get; }
        public double PixelSizeX { get; }
        public double PixelSizeY { get; }
        public string Projection { get; } // 保存投影信息
        public SpatialReference SRS { get; } // 保存空间参考系统
        public double[] GeoTransform { get; } // 保存地理变换参数

        public FileGeoInfos(Dataset dataset, string filePath)
        {
            FilePath = filePath;

            double[] transform = new double[6];
            dataset.GetGeoTransform(transform);
            GeoTransform = transform; // 保存地理变换参数

            Projection = dataset.GetProjection();
            SRS = new SpatialReference(Projection);

            PixelSizeX = transform[1];
            PixelSizeY = Math.Abs(transform[5]);

            MinX = transform[0];
            MaxX = transform[0] + dataset.RasterXSize * transform[1];
            MinY = transform[3] + dataset.RasterYSize * transform[5];
            MaxY = transform[3];
        }
    }

    // 统一网格系统
    public class UnifiedGridSystem
    {
        public GridMetadata GridInfo { get; }
        public int TileSize { get; }
        public string BaseName { get; }

        public UnifiedGridSystem(List<FileGeoInfos> fileInfos, int tileSize)
        {
            if (fileInfos == null || fileInfos.Count == 0)
                throw new ArgumentException("需要至少一个文件信息");

            TileSize = tileSize;
            BaseName = Path.GetFileNameWithoutExtension(fileInfos[0].FilePath)
                        .Split('_')[0]; // 使用第一个文件的基础名

            // 计算所有文件的最小外包矩形
            double overallMinX = fileInfos.Min(f => f.MinX);
            double overallMaxX = fileInfos.Max(f => f.MaxX);
            double overallMinY = fileInfos.Min(f => f.MinY);
            double overallMaxY = fileInfos.Max(f => f.MaxY);

            // 使用第一个文件的分辨率作为基准
            double pixelSizeX = fileInfos[0].PixelSizeX;
            double pixelSizeY = fileInfos[0].PixelSizeY;

            // 计算网格参数
            double tileGeoSizeX = tileSize * pixelSizeX;
            double tileGeoSizeY = tileSize * pixelSizeY;

            int cols = (int)Math.Ceiling((overallMaxX - overallMinX) / tileGeoSizeX);
            int rows = (int)Math.Ceiling((overallMaxY - overallMinY) / tileGeoSizeY);

            // 创建网格元数据
            GridInfo = new GridMetadata
            {
                FilePaths = fileInfos.Where(f => !string.IsNullOrEmpty(f.FilePath)).Select(f => f.FilePath).ToList(),
                MinX = overallMinX,
                MaxX = overallMaxX,
                MinY = overallMinY,
                MaxY = overallMaxY,
                PixelSizeX = pixelSizeX,
                PixelSizeY = pixelSizeY,
                TileSize = tileSize,
                Rows = rows,
                Cols = cols,
                Cells = new List<GridCellInfo>()
            };

            // 获取第一个文件的投影用于坐标转换
            SpatialReference srcSrs = fileInfos[0].SRS;
            double[] firstTransform = fileInfos[0].GeoTransform;

            // 创建目标坐标系 (WGS84)
            using (SpatialReference destSrs = new SpatialReference(""))
            {
                destSrs.SetWellKnownGeogCS("WGS84");
                destSrs.SetAxisMappingStrategy(AxisMappingStrategy.OAMS_TRADITIONAL_GIS_ORDER);

                // 创建坐标转换器
                using (CoordinateTransformation ct = new CoordinateTransformation(srcSrs, destSrs))
                {
                    // 创建网格单元并转换坐标
                    for (int row = 0; row < rows; row++)
                    {
                        for (int col = 0; col < cols; col++)
                        {
                            double minX = overallMinX + col * tileGeoSizeX;
                            double maxX = minX + tileGeoSizeX;
                            double minY = overallMinY + row * tileGeoSizeY;
                            double maxY = minY + tileGeoSizeY;

                            var topLeft = new GeoPoint { X = minX, Y = maxY };
                            var bottomRight = new GeoPoint { X = maxX, Y = minY };
                            var center = new GeoPoint
                            {
                                X = (minX + maxX) / 2,
                                Y = (minY + maxY) / 2
                            };

                            // 转换为经纬度坐标
                            ConvertToWGS84(ct, topLeft);
                            ConvertToWGS84(ct, bottomRight);
                            ConvertToWGS84(ct, center);
                            // 计算在第一个文件中的像素起点坐标
                            int pixelX = (int)Math.Round((minX - firstTransform[0]) / firstTransform[1]);
                            int pixelY = (int)Math.Round((maxY - firstTransform[3]) / firstTransform[5]);
                            GridInfo.Cells.Add(new GridCellInfo
                            {
                                Row = row,
                                Col = col,
                                PixelX = pixelX, // 在第一个文件中的像素X起点
                                PixelY = pixelY, // 在第一个文件中的像素Y起点
                                TopLeft = topLeft,
                                BottomRight = bottomRight,
                                Center = center,
                                FileTypes = new List<string>()
                            });
                        }
                    }
                }
            }
        }

        // 坐标转换方法 - 修复经纬度顺序
        private static void ConvertToWGS84(CoordinateTransformation ct, GeoPoint point)
        {
            double[] output = new double[3];
            ct.TransformPoint(output, point.X, point.Y, 0);

            // 正确存储经纬度: output[0] = 经度, output[1] = 纬度
            point.Lon = output[0];
            point.Lat = output[1];
        }
    }

    // 网格元数据
    public class GridMetadata
    {
        public List<string> FilePaths { get; set; }
        public double MinX { get; set; } // 投影坐标X最小值
        public double MaxX { get; set; } // 投影坐标X最大值
        public double MinY { get; set; } // 投影坐标Y最小值
        public double MaxY { get; set; } // 投影坐标Y最大值
        public double PixelSizeX { get; set; } // X方向像素大小
        public double PixelSizeY { get; set; } // Y方向像素大小
        public int TileSize { get; set; } // 切片大小
        public int Rows { get; set; } // 网格行数
        public int Cols { get; set; } // 网格列数
        public List<GridCellInfo> Cells { get; set; } // 网格单元列表
    }

    // 网格单元信息
    public class GridCellInfo
    {
        public int Row { get; set; } // 行索引
        public int Col { get; set; } // 列索引
        public int PixelX { get; set; }  // 像素坐标起点X
        public int PixelY { get; set; }  // 像素坐标起点Y
        public GeoPoint TopLeft { get; set; } // 左上角坐标
        public GeoPoint BottomRight { get; set; } // 右下角坐标
        public GeoPoint Center { get; set; } // 中心点坐标
        public List<string> FileTypes { get; set; } // 包含的文件类型
    }

  
}